# coding: utf-8
#
from logging import fatal
import uiautomator2 as u2
import time
from logzero import logger
from uiautomator2 import Device
from uiautomator2 import xpath
from uiautomator2.exceptions import UiObjectNotFoundError
from . import app_settings
from .enums import PageName
from ..errors import StartError, StopError, OperationError
from .uiadapter import Adapter, UIKey
class App(object):
    def __init__(self, device: Device):
        """ 初始化WechatApp管理状态
            Args:
                package_name (str): 微信package_name
                activity (str): app activity
        """
        self._device = device
        self.app_name = app_settings.APP_NAME
        self.package_name = app_settings.PACKAGE_NAME
        self.activity = app_settings.LANUCH_ACTIVITY
        self._version = 'v' + self.get_info()['version']
        self._uiadapter = Adapter(self._version)
        if not self._uiadapter.check_version(self._version):
            raise OperationError("不支持当前app版本, 版本号:%s", self._version)
        
        self._status = "INITIALED"
        self._activated = False
        self._current_task = None
        self._retry_start_count = 0

    def get_info(self):
        info = self._device.app_info(self.package_name)
        # info include the follow data.
        # {'packageName': 'com.tencent.mm',
        #  'mainActivity': 'com.tencent.mm.ui.LauncherUI', 
        #  'label': '微信', 
        #  'versionName': '7.0.13', 
        #  'versionCode': 1640, '
        #  size': 135588342}
        return {
            "pakcage":info["packageName"],
            "activity":info["mainActivity"],
            "version":info["versionName"]
        }

    def start(self, with_home_page: bool=False):
        """ 打开微信
            Args:
                with_home_page (bool): 打开微信并呈现首页，“微信”选中页
            Error: 
                StartError
        """
        # Try to start the wx app
        self._device.app_start(package_name = self.package_name, 
                                activity = self.activity, 
                                wait = True, 
                                stop = False, 
                                use_monkey=False)
        self._device.screen_on()

        # Todo: 
        self._device.app_wait(self.package_name, timeout=10, front=True)
        
        if(with_home_page == True and self.is_homepage() == False):
            self._device.app_start(package_name = self.package_name, 
                                activity = self.activity, 
                                wait = True, 
                                stop = True, 
                                use_monkey=False)
            self._device.app_wait(self.package_name, timeout=10, front=True)
        time.sleep(1)
        logger.info("设备 %s 的微信程序正在打开中...", self._device.serial)
        self._status = "STARTED" 

    def stop(self):
        """ 关闭微信 Stop one application: am force-stop.
        """
        try:
            self._device.app_stop(self.package_name)
            self._status = "STOPPED"
        except StopError:
            self._status = "INITIALED"

    def wait_homepage(self):
        # Check if the homepage has been present.
        deadline = time.time() + 10
        while(self.is_homepage() == False):
            if(time.time() >= deadline):
                break
        # if homepage has not been present, Retry opening the wx app in 3 times.
        # if still not bring homepage on front, throw the error.
        if (self.is_homepage() == False):
            if (self._retry_start_count < 3):
                self._retry_start_count = self._retry_start_count + 1
                self.start(True)
            else: 
                raise StartError()
        
        self._retry_start_count = 0
        logger.info("设备 %s 的微信程序打开完毕。", self._device.serial)

    def get_current_postion(self) -> str:
        if(self.is_homepage()):
            return PageName.HOME_PAGE
        if(self.is_contactbook()):
            return PageName.CONTACT_BOOK
        if(self.is_contact_chatroom()):
            return PageName.CONTACT_CHAT_ROOM
        if(self.is_chating()):
            return PageName.CHATING
        if(self.is_chatroom_info()):
            return PageName.CHAT_ROOM_INFO
        if(self.is_room_member_info()):
            return PageName.ROOM_MEMBERS
        if(self.is_contact_info()):
            return PageName.CONTACT_INFO
        if(self.is_contact_permission()):
            return PageName.CONTACT_PERMISSION
        
        return ""

    def is_app_opened(self) -> bool:
        return self.is_homepage() or \
            self.is_contactbook() or \
            self.is_contact_chatroom() or \
            self.is_chating() or \
            self.is_chatroom_info() or \
            self.is_room_member_info() or \
            self.is_contact_info() or \
            self.is_contact_permission() or \
            self.is_settings() or \
            self.is_settings_privacy() or \
            self.is_new_friend() or \
            self.is_add_more_friend() or \
            self.is_search_more_friends()
            
    def is_homepage(self) -> bool:
        """
            POSTION: 01 
            检查是否处于首页
            通过匹配首页头部和底部“微信”的数量是否大于等于2来判断是否处于首页。
            xpath("//*[starts-with(@text, '微信')]")
            Return:
                result: 返回微信是否为当前激活状态的app。
        """
        if(self.device.app_current()["activity"] == "com.tencent.mm.ui.LauncherUI" 
            and len(self._device.xpath("//*[starts-with(@text, '微信')]").all()) >= 2):
            return True
        return False

    def is_contactbook(self) -> bool:
        """ 
            POSTION: 02 
            点击“通讯录”后呈现的通讯录界面。
            Return:
                result: 返回微信是否为当前激活状态的app。
        """
        if(self.device.app_current()["activity"] == "com.tencent.mm.ui.LauncherUI" 
            and len(self._device.xpath("//*[starts-with(@text, '通讯录')]").all()) >= 2):
            return True
        return False

    def is_contact_chatroom(self) -> bool:
        """
            POSTION: 03
            通讯录中的群聊界面
        """
        if(self.device.app_current()["activity"] == "com.tencent.mm.ui.contact.ChatroomContactUI"):
            return True
        return False

    def is_chating(self) -> bool:
        """
            POSTION: 04
            聊天界面
        """
        adapter = self._uiadapter
        if(self.device.app_current()["activity"] == "com.tencent.mm.ui.chatting.ChattingUI"):
            return True

        # 7.0.19 以下次序为语音按键、文本框、表情按键、加号按键
        if self.device.app_current()["activity"] == "com.tencent.mm.ui.LauncherUI" and \
            self.device(adapter.get(UIKey.UIVoiceImageButton_RI)).exists and \
            self.device(adapter.get(UIKey.UIChattingTextBox_RI)).exists and \
            self.device(adapter.get(UIKey.UIEmojoImageButton_RI)).exists and \
            self.device(adapter.get(UIKey.UIAddImageButton_RI)).exists:
            return True

        return False

    def is_chatroom_info(self) -> bool:
        """
            POSTION: 05
            群房间信息界面（包含群部分群成员）。
        """
        if(self.device.app_current()["activity"] == "com.tencent.mm.chatroom.ui.ChatroomInfoUI"):
            return True
        return False
    
    def is_room_member_info(self) -> bool:
        """
            POSTION: 06
            包含全部群成员的界面。
        """
        if(self.device.app_current()["activity"] == "com.tencent.mm.chatroom.ui.SeeRoomMemberUI"):
            return True
        return False

    def is_contact_info(self) -> bool:
        """
            POSTION: 06
            联系人信息界面。
        """
        if(self.device.app_current()["activity"] == "com.tencent.mm.plugin.profile.ui.ContactInfoUI"):
            return True
        return False

    def is_contact_permission(self) -> bool:
        """
            POSTION: 07
            申请添加朋友界面，向朋友发起添加请求。
        """
        if(self.device.app_current()["activity"] == "com.tencent.mm.plugin.profile.ui.SayHiWithSnsPermissionUI"):
            return True
        return False
   
    def is_settings(self) -> bool:
        """
            
            我->设置界面
        """
        if(self.device.app_current()["activity"] == "com.tencent.mm.plugin.setting.ui.setting.SettingsUI"):
            return True
        return False

    def is_settings_privacy(self) -> bool:
        if(self.device.app_current()["activity"] == "com.tencent.mm.plugin.setting.ui.setting.SettingsPrivacyUI"):
            return True
        return False

    def is_new_friend(self) -> bool:
        '''
        '''
        if(self.device.app_current()["activity"] == "com.tencent.mm.plugin.subapp.ui.friend.FMessageConversationUI"):
            return True
        return False
   
    def is_add_more_friend(self) -> bool:
        '''
        '''
        if(self.device.app_current()["activity"] == "com.tencent.mm.plugin.subapp.ui.pluginapp.AddMoreFriendsUI"):
            return True
        return False
   
    def is_search_more_friends(self) -> bool:
        '''
        '''
        if(self.device.app_current()["activity"] == "com.tencent.mm.plugin.fts.ui.FTSAddFriendUI"):
            return True
        return False

    
    def tap_back(self):
        self._device.press("back")

    @property
    def status(self):
        return self._status
    
    @property
    def device(self):
        return self._device

class GroupRoom(object):
    def __init__(self, app: App):
        """ 初始化WechatApp管理状态
            Args:
                package_name (str): 微信package_name
                activity (str): app activity
        """
        self._app = app
        self._group_list = None
        self._status = "NONE"

    
    def navigate_contacts(self):
        d = self._app.device
        adapter = self._app._uiadapter
        d.xpath(adapter.get(UIKey.TextContact_xPath)).click()
        # d.xpath('//*[@text="通讯录"]').click()
        self._status = "CONTACTS_OPEN"
        logger.info("设备 %s 的微信程序位于 %s界面。", d.serial, "通讯录")

    def navigate_groups(self):
        d = self._app.device
        d.xpath('//*[@text="群聊"]').click()
        self._status = "CONTACTS_OPEN"
        logger.info("设备 %s 的微信程序位于 %s界面。", d.serial, "聊天信息（群聊列表）")

    def collect_groups(self, refresh_list: bool):
        if(self._group_list == None):
            self._group_list = self._collect_groups()
        else:
            if(refresh_list):
                self._group_list = self._collect_groups()
        return self._group_list
    
    def navigate_chating(self, group_index: int):
        """
           定位指定的群列表项目并打开群聊天界面
           Args:
            group_index: 指定的群的索引
        """
        d = self._app.device
        count = len(self._group_list)
        if(group_index > count or group_index < 0):
            raise OperationError()

        logger.info("设备 %s 的微信程序正进入 %s 群位于 %s界面。", d.serial, self._group_list[group_index], "群聊天")
        d(className="android.widget.ListView", resourceId="com.tencent.mm:id/f4") \
            .child_by_text(self._group_list[group_index], 
                            allow_scroll_search=True, 
                            className="android.widget.TextView") \
                                .click()

        logger.info("设备 %s 的微信程序已经进入 %s 群位于 %s界面。", d.serial, self._group_list[group_index], "群聊天")

    def navigate_new_friends(self):
        d = self._app.device
        adapter = self._app._uiadapter
        # 点击 + 号
        if d(resourceId=adapter.get(UIKey.UIAddImage_RI)).exists:
            d(resourceId=adapter.get(UIKey.UIAddImage_RI)).click()
            time.sleep(1)
        if d.xpath(adapter.get(UIKey.TextAddNewFriend_xPath)).exists:
            d.xpath(adapter.get(UIKey.TextAddNewFriend_xPath)).click()
        # if d.xpath('//*[@text="新的朋友"]').exists:
        #     d.xpath('//*[@text="新的朋友"]').click()
        # if d.xpath(adapter.get(UIKey.TextNewFriend_xPath)).exists:
        #     d.xpath(adapter.get(UIKey.TextNewFriend_xPath)).click()   
        

    def navigate_add_friends(self):
        d = self._app.device
        adapter = self._app._uiadapter
       
        if d.xpath(adapter.get(UIKey.TextAddNewFriend_xPath)).exists:
            d.xpath(adapter.get(UIKey.TextAddNewFriend_xPath)).click()
        if d.xpath('//*[@text="添加朋友"]').exists:
            d.xpath('//*[@text="添加朋友"]').click()
    def entry_search(self):
        d = self._app.device
        adapter = self._app._uiadapter
        
        if d(resourceId=adapter.get(UIKey.UISearchFriend_RI)).exists:
            d(resourceId=adapter.get(UIKey.UISearchFriend_RI)).click()
        # if d(resourceId="com.tencent.mm:id/f9j").exists:
        #     d(resourceId="com.tencent.mm:id/f9j").click()

    def fillin_text(self, phone:str):
        d = self._app.device
        adapter = self._app._uiadapter
        if d(resourceId=adapter.get(UIKey.UISearchTextBox_RI)).exists:
            d(resourceId=adapter.get(UIKey.UISearchTextBox_RI)).set_text(phone)
        # if d(resourceId="com.tencent.mm:id/bfl").exists:
        #     d(resourceId="com.tencent.mm:id/bfl").set_text(phone)
    
    def select_search_result(self, phone:str):
        d = self._app.device
        expr = '//*[@text="搜索:' + phone + '"]'
        if d.xpath(expr).exists:
            d.xpath(expr).click()
    
    def check_search_result(self):
        d = self._app.device
        expr = '//*[@text="该用户不存在"]'
        if d.xpath(expr).exists:
            return True
        return False
        
    def _count_groups(self) -> int:
        txt = ""
        count = 0
        d = self._app.device
        scrollable = d(className="android.widget.ListView", resourceId="com.tencent.mm:id/f4")
        scrollable.wait(timeout = 0.5)
        scrollable.scroll.toEnd()
        scrollable.wait(timeout = 0.5)
       
        if(d(resourceId="com.tencent.mm:id/ax_").exists):
            txt = d(resourceId="com.tencent.mm:id/ax_").get_text()
            if(txt[0:-3].isnumeric()):
                count = int(txt[0:-3])
        scrollable.scroll.toBeginning()
        logger.info("设备 [ %s ] 的微信程序收集的群总数为 [ %s ]-[ %s界面 ]。", d.serial, count, "群聊列表")
        return count
    
    def _collect_groups(self) -> list:
        
        d = self._app.device
        logger.info("设备 %s 的微信程序开始收集群信息-%s界面。", d.serial, "聊天信息（群聊列表）")
        count = self._count_groups()
        # 模拟滚动的不长，需要根据一页显示的群数来确定， 当前屏幕的行数 * 经验值
        steps = count/12*6
        last_lst = []
        scrollable = d(className="android.widget.ListView", resourceId="com.tencent.mm:id/f4")
        lst = []
        self._collect_groups_per_page(lst, last_lst, scrollable, steps)

        scrollable.scroll.toBeginning()
        scrollable.wait(timeout = 0.5)
        logger.info("设备 %s 的微信程序结束收集群信息, 群数量为[ %s ]- [ %s界面 ]。", d.serial, len(lst), "聊天信息（群聊列表）")
        time.sleep(0.5)
        return lst

    def _collect_groups_per_page(self, lst, last_lst, scrollable, steps):
        """
            收集每一页的群组项目并添加到集合中。
            Args:
                lst: 群列表
                scrollable: 包含群的外部控件容器。
                steps: 滚动的步长
        """
        #用于检查上一次控件集合和当前控件集合是否完全一致。
        d = self._app.device
        view_lst = d(className="android.widget.TextView", resourceId="com.tencent.mm:id/b10")
        current_lst = []
        for item in view_lst:
            txt = item.get_text()
            current_lst.append(txt)
            if txt not in lst:
                lst.append(txt)
        if (self._lst_equal(current_lst, last_lst) == False):
            last_lst = list(current_lst)
            scrollable.scroll(steps=steps)
            self._collect_groups_per_page(lst, last_lst, scrollable, steps)

    def _lst_equal(self, current_lst, last_lst):
        """

        """
        if(current_lst != last_lst):
            return False
        return True
    
    @property
    def groups(self) -> list:
        return self._group_list

    @groups.setter
    def groups(self, value: list):
        self._group_list = value

    
class Contact(object):
    def __init__(self, app: App):
        """ 初始化WechatApp管理状态
            Args:
                package_name (str): 微信package_name
                activity (str): app activity
        """
        self._app = app
        self._status = "NONE"
        self._member_lst = None

    def navigate_chat_info(self, refresh_list: bool) -> list:
        """
            聊天中点击'...'导航到聊天信息界面。
        """
        d = self._app.device
        d.xpath('//android.support.v7.widget.LinearLayoutCompat').click()
        time.sleep(0.5)
        #以下方法可能导致界面跳转到聊天成员界面
        self._navigate_chat_members()

        if(self._app.is_chatroom_info()):
            #收集成员列表信息
            if(self._member_lst == None):
                logger.info("正在聊天信息界面中收集成员。")
                self._member_lst = self._collect_members("android:id/list", "com.tencent.mm:id/ezs")
                logger.info("正在聊天信息界面中收集成员完毕，成员数量为：%s", len(self._member_lst))
            else:
                if(refresh_list):
                    logger.info("正在聊天信息界面中重新收集成员。")
                    self._member_lst = self._collect_members("android:id/list", "com.tencent.mm:id/ezs")
                    logger.info("正在聊天信息界面中收集成员完毕，成员数量为：%s", len(self._member_lst))
                else:
                    logger.info("正在使用缓存中的成员列表。成员数量为：%s", len(self._member_lst))

        if(self._app.is_room_member_info()):
            #收集成员列表信息
            if(self._member_lst == None):
                logger.info("正在聊天成员界面中收集成员。")
                self._member_lst = self._collect_members("com.tencent.mm:id/ahx", "com.tencent.mm:id/f6h")
                logger.info("正在聊天成员界面中收集成员完毕，成员数量为：%s", len(self._member_lst))
            else:
                if(refresh_list):
                    logger.info("正在聊天成员界面中重新收集成员。")
                    self._member_lst = self._collect_members("com.tencent.mm:id/ahx", "com.tencent.mm:id/f6h")
                    logger.info("正在聊天成员界面中收集成员完毕，成员数量为：%s", len(self._member_lst))

        return self._member_lst
        
    def navigate_member_info(self, member_index):
        d = self._app.device
        if(self._app.is_chatroom_info()):
            try:
                d(className="android.widget.ListView", resourceId="android:id/list") \
                    .child_by_text(self._member_lst[member_index], allow_scroll_search=True, className="android.widget.TextView") \
                        .click()
                logger.info("在聊天信息界面中找到‘%s’的成员。", self._member_lst[member_index])
            except(UiObjectNotFoundError):
                logger.info("在聊天信息界面中未找到‘%s’的成员。", self._member_lst[member_index])
        if(self._app.is_room_member_info()):     
            try:

                d(className="android.widget.GridView", resourceId="com.tencent.mm:id/ahx") \
                    .child_by_text(self._member_lst[member_index], allow_scroll_search=True, className="android.widget.TextView") \
                        .click()
                logger.info("在聊天成员界面中找到‘%s’的成员。", self._member_lst[member_index])
            except(UiObjectNotFoundError):
                logger.info("在聊天成员界面中未找到‘%s’的成员。", self._member_lst[member_index])

    def send_friend_invitation(self) -> bool:
        """
            发送添加好友请求。这里有两种情况：
            1. 用户没有要求审核申请，那么在联系人信息界面就可以直接点击“添加到通讯录”
            2. 用户要求审核申请，那么就需要跳转到“申请添加朋友”进行申请，之后自动会跳回到联系人信息界面。
            3. 对方设置隐私设置，无法添加。
        """
        too_fast = False
        d = self._app.device
        adapter = self._app._uiadapter
        if(d.xpath(adapter.get(UIKey.TextAddToContact_xPath)).exists):
            # 如果好友没有打开需要好友验证，则会在当前页面直接发送添加好友信息。
            d.xpath(adapter.get(UIKey.TextAddToContact_xPath)).click()
            logger.info("在用户信息界面中尝试发送好友申请 - 好友界面。")
            time.sleep(2)
        # d(resourceId="com.tencent.mm:id/dm4").exists -> ‘提示’文本框
        # d(resourceId="com.tencent.mm:id/dm3").exists -> ‘提示’对话框中的“确定”按钮
        if(self._app.is_contact_info() and d(resourceId="com.tencent.mm:id/dm4").exists):
            d(resourceId="com.tencent.mm:id/dm3").click()
            time.sleep(1)
            
        if(self._app.is_contact_permission() and d.xpath(adapter.get(UIKey.TextApplicationAddingFriend_xpath)).exists):
            # 从申请添加好友界面点击“发送”会自动返回到好友信息页面。
            d.xpath(adapter.get(UIKey.TextSendInAddingFriend_xpath)).click()
        # d(resourceId="com.tencent.mm:id/dm4").exists -> ‘申请添加朋友’文本框
        # if(self._app.is_contact_permission() and d.xpath('//*[@text="申请添加朋友"]').exists):
        #     d.xpath('//*[@text="发送"]').click()
            # time.sleep(0.5)
            # This will be waited for at least 5 seconds.
            if not "Short message" in d.toast.get_message(5.0, default="微信：操作过于频繁，请稍后再试。") is None:
                too_fast = True
            logger.info("在尝试发送好友申请 - 申请添加好友界面。")
            time.sleep(1)
        return too_fast
        
    def _navigate_chat_members(self):
        """
            如果 “查看全部群成员”将导航到聊天成员界面，否则停留在之前的界面
        """
        d = self._app.device
        try:
            d(className="android.widget.ListView", resourceId="android:id/list") \
                .child_by_text("查看全部群成员", allow_scroll_search=True, className="android.widget.TextView") \
                    .click()
        except(UiObjectNotFoundError):
            #以上语句将导致界面下滑，将界面重新恢复到之前状态
            d(resourceId="android:id/list").scroll.toBeginning()
            logger.info("在聊天信息界面中未找到‘查看全部群成员’，未做跳转。")

    def _count_members(self) -> int:
        """
            计算群成员的数量，在"聊天消息"或"聊天成员"界面都可以使用此方法。
            Returns: 成员数量
        """
        txt = ""
        count = 0
        d = self._app.device
        
        if(self._app.is_chatroom_info()):
            txt = d.xpath("//*[starts-with(@text, '聊天信息')]").text
        if(self._app.is_room_member_info()):
            txt = d.xpath("//*[starts-with(@text, '聊天成员')]").text

        if(txt[5:-1].isnumeric()):
            count = int(txt[5:-1])

        return count
    
    def _collect_members(self, scrollable_resource_id: str, text_resource_id: str) -> list:
        d = self._app.device
        count = self._count_members()
        # 模拟滚动的不长，需要根据人数来确定 人数 / 每行的人数 / 当前屏幕的行数 * 经验值
        steps = count/35*4
        last_lst = list()
        scrollable = d(resourceId=scrollable_resource_id)
        can_scroll = count > 7 * 5
        lst = list()

        self._collect_item_per_page(lst, last_lst, scrollable, steps, text_resource_id, can_scroll)
        logger.info(lst)
        return lst
    
    def _collect_item_per_page(self, lst, last_lst, scrollable, steps, resource_id:str, can_scroll:bool):
        """
            收集每一页的群成员并添加到集合中。
            Args:
                lst: 群成员列表
                scrollable: 包含群成员的外部控件容器。
                steps: 滚动的步长
        """
        #用于检查上一次控件集合和当前控件集合是否完全一致。
        d = self._app.device
        view_lst = d(className="android.widget.TextView", resourceId= resource_id)
        current_lst = []
        for item in view_lst:
            txt = item.get_text()
            current_lst.append(txt)
            if txt not in last_lst: # lst:
                lst.append(txt)
        time.sleep(0.5)
        # 检查当前集合和上一次集合元素是否完全相同。如果相同，表示达到最后一页。
        if (self._lst_equal(current_lst, last_lst) == False):
            last_lst = list(current_lst)
            if(can_scroll):
                scrollable.scroll(steps=steps)
            self._collect_item_per_page(lst, last_lst, scrollable, steps, resource_id, can_scroll)

    @property
    def members(self) -> list:
        '''只包含指定 group index的成员'''
        return self._member_lst

    @members.setter
    def members(self, value: list):
        self._member_lst = value

    def _lst_equal(self, current_lst, last_lst):
        """

        """
        if(current_lst != last_lst):
            return False
        return True

class AboutMy(object):
    def __init__(self, app: App):
        """ 初始化WechatApp管理状态
            Args:
                package_name (str): 微信package_name
                activity (str): app activity
        """
        self._app = app
        
    def disabled_friend_permission(self):
        '''
            禁用“加我为朋友时需要验证”， 如果此选项为勾选状态。
        '''
        d = self._app.device
        if d.xpath('//*[@text="我"]').exists:
            d.xpath('//*[@text="我"]').click()
        
        time.sleep(0.5)
        
        if d.xpath('//*[@text="设置"]').exists:
            d.xpath('//*[@text="设置"]').click()
            time.sleep(0.5)

        if self._app.is_settings() and d.xpath('//*[@text="隐私"]').exists:
            d.xpath('//*[@text="隐私"]').click()
            time.sleep(0.5)

        if self._app.is_settings_privacy() and \
            not d(resourceId="com.tencent.mm:id/ap1", description="已关闭").exists:
            d(className='android.view.View', resourceId='com.tencent.mm:id/ap1').click()